import { getImportSection, setImportSection, getEmitFns, setEmitFns, getDomList, setDomList, getHooks, setHooks, getCurFilePath } from './formatFile.js'
import { formatNextTick } from './formatNextTick.js'
import { originAddress } from '../config/importOrigin.js'
import fs from 'fs'

export const importFromOrigin = (name, origin = 'vue') => {
  const reg = new RegExp('import.*?' + name + '.*?from', 'g')
  const importSection = getImportSection()
  if (importSection.search(reg) < 0) {
    setImportSection(`import {${name}} from '${origin}'\n` + importSection)
  }
}

export const matchBracketEnd = (str) => {
  let restNeedMatchBracketNum = 'initVal'
  let result = ''
  for (let i = 0; i < str.length; i++) {
    result += str[i]
    if (['{', '['].includes(str[i])) restNeedMatchBracketNum !== 'initVal' ? restNeedMatchBracketNum++ : (restNeedMatchBracketNum = 1)
    else if (['}', ']'].includes(str[i])) restNeedMatchBracketNum--
    if (restNeedMatchBracketNum === 0) return result
  }
  return result
}

export const removeInvalid = (str) => {
  str = str.replace(/(export default \{\n)/g, '')
  str = str.replace(/(\})\n<\/script>/g, '')
  str = str.replace(/(name: ['|"][a-zA-Z]+['|"],\n)/g, '')
  str = str.replace(/(components:.*?\n)/g, '')
  return str
}

export const splitMethods = (str) => {
  str = str
    .split('\n')
    .filter((item) => !item.trim().startsWith('//'))
    .join('\n')
  let result = []
  let bracketNum = 'initVal'
  let func = ''
  for (let i = 0; i < str.length; i++) {
    func += str[i]
    if (str[i] === '{') {
      if (bracketNum === 'initVal') bracketNum = 0
      bracketNum++
    } else if (str[i] === '}') {
      bracketNum--
    }
    if (str[i] === ',' && bracketNum === 0) {
      result.push(func.substring(0, func.length - 1))
      func = ''
      bracketNum = 'initVal'
    }
  }
  result.push(func.at(-1) === ',' ? func.substring(0, func.length - 1) : func)
  return result.map((item) => item.trim()).filter((item) => item)
}

const formatCamelCase = (str) => {
  return str
    .replace(/[^a-zA-Z0-9]+/g, ' ')
    .toLowerCase()
    .replace(/(?:^\w|[A-Z]|\b\w)/g, function (word, index) {
      return index === 0 ? word.toLowerCase() : word.toUpperCase()
    })
    .replace(/\s/g, '')
}

const formatMethodCallOrAssign = (str) => {
  // to do 当两种不同的this类型出现在同一行的时候，处理有误。比如 this.value = this.method()这种
  str = str.replace(/this\.(\w+)\(/g, '$1(')
  str = str.replace(/this\.(\w+)/g, '$1.value')
  return str
}

export const formatThisWithDot = (str, section) => {
  if (!str) return ''
  str = str.trim()
  if (str.startsWith('//')) return str // to do 有的注释会被删除掉
  const isThisCallGlobal = str.search(/this.\$/) >= 0
  if (isThisCallGlobal) {
    const type = str.replace(/(.*?)this\.\$(\w+)(.*)/gi, '$2')
    const hooks = getHooks()
    switch (type) {
      case 'emit':
        const emitName = str.replace(/this\.\$emit\('(\w+)'(.*?)\)/g, '$1')
        const emitFns = getEmitFns()
        if (!emitFns.includes(emitName.trim())) {
          setEmitFns([...emitFns, emitName.trim()])
        }
        str = str.replace(/this\.\$emit\('(\w+)'(.*?)\)/g, `emits('$1'$2)`)
        break
      case 'refs':
        // to do 遇到拼接的那种会匹配不到 ，比如 const matchedElement = this.$refs[`chart-${this.chartId}`][0]
        const isDotSplit = str.search(/this\.\$refs\[/) < 0
        const isDomNameVariable = str.search(/this\.\$refs\[`/) >= 0
        if (isDomNameVariable) {
          const refsError = {
            path: getCurFilePath(),
            str,
            reason: 'refs dom is variable',
            section,
          }
          missContentWriteToFile(JSON.stringify(refsError))
        } else {
          const refsName = formatCamelCase(str.replace(/^(.*?)this\.\$refs(\.|\[('|"))(.+?)(\.|('|")\])(.*?)$/g, '$4'))
          str = str.replace(/this\.\$refs(\.|\[('|"))(.+?)(\.|('|")\])/g, `${refsName}.value${isDotSplit ? '.' : ''}`)
          const domList = getDomList()
          if (!domList.includes(refsName.trim())) {
            setDomList([...domList, refsName.trim()])
          }
        }
        break
      case 'store':
        str = str.replace(/this\.\$store/g, 'store')
        if (!hooks.find((item) => item.name === 'store')) {
          setHooks([...hooks, { name: 'store', method: 'useStore' }])
        }
        importFromOrigin('useStore', originAddress.store)
        break
      case 'router':
        str = str.replace(/this\.\$router/g, 'router')
        if (!hooks.find((item) => item.name === 'router')) {
          setHooks([...hooks, { name: 'router', method: 'useRouter' }])
        }
        importFromOrigin('useRouter', originAddress.router)
        break
      case 'route':
        str = str.replace(/this\.\$route/g, 'route')
        if (!hooks.find((item) => item.name === 'route')) {
          setHooks([...hooks, { name: 'route', method: 'useRoute' }])
        }
        importFromOrigin('useRoute', originAddress.route)
        break
      case 'message':
        str = str.replace(/this\.\$message/g, 'ElMessage')
        importFromOrigin('ElMessage', originAddress.message)
        break
      case 'alert':
        str = str.replace(/this\.\$alert/g, 'ElAlert')
        importFromOrigin('ElAlert', originAddress.alert)
        break
      case 'eventHub':
        const params = str.replace(/this\.\$eventHub\.\$(\w+)(.*)/g, `$2`).replace(/this\./g, '')
        str = str.replace(/this\.\$eventHub\.\$(\w+)(.*)/g, `emitter.$1$${params}`)
        if (!hooks.find((item) => item.name === 'emitter')) {
          setHooks([...hooks, { name: 'emitter', method: 'mitt' }])
        }
        importFromOrigin('mitt', originAddress.mitt)
        break
      case 'get':
        // to do 处理全局请求的方法
        break
      case 'post':
        // to do 处理全局请求的方法
        break
      case 'put':
        // to do 处理全局请求的方法
        break
      case 'del':
        // to do 处理全局请求的方法
        break

      default:
        // to do $parents $children $listeners $set 等等
        const err = {
          path: getCurFilePath(),
          str,
          reason: 'should format global method',
          section,
        }
        missContentWriteToFile(JSON.stringify(err))
        break
    }
    if (str.search(/this\./) >= 0) {
      str = formatMethodCallOrAssign(str)
    }
  } else {
    str = formatMethodCallOrAssign(str)
  }
  return str
}

export const deconstructionFn = (str, section) => {
  const normalMatched = str.match(/^(\w+)(async)?\((.*?)\)\s?\{([\s\S]*?)\s*?\}$/)
  const syncMatched = str.match(/^(\w+)\s?:\s?(async)?\s?(function)?\s?\((.*?)\)\s?\{([\s\S]*?)\s*?\}$/)
  if (normalMatched) {
    const name = normalMatched[1]
    const isAsync = !!normalMatched[2]
    const params = normalMatched[3]
    const body = normalMatched[4]
    const { str: bodyWithoutNextTick, isAsync: isNextAsync } = formatNextTick(body)
    const formattedBody = bodyWithoutNextTick
      ? bodyWithoutNextTick
          .split('\n')
          .filter((item) => !item.trim().startsWith('//'))
          .map((item) => formatThisWithDot(item, section))
          .filter((item) => item)
          .join('\n')
      : ''
    return { name, params, body: formattedBody, isAsync: isAsync || isNextAsync }
  } else if (syncMatched) {
    const name = syncMatched[1]
    const params = syncMatched[4]
    const isAsync = !!syncMatched[2]
    const body = syncMatched[5]
    const { str: bodyWithoutNextTick, isAsync: isNextAsync } = formatNextTick(body)
    const formattedBody = bodyWithoutNextTick
      ? bodyWithoutNextTick
          .split('\n')
          .filter((item) => !item.trim().startsWith('//'))
          .map((item) => formatThisWithDot(item, section))
          .filter((item) => item)
          .join('\n')
      : ''
    return { name, params, body: formattedBody, isAsync: isAsync || isNextAsync }
  } else {
    const err = {
      path: getCurFilePath(),
      str,
      reason: 'cannot deconstructionFn',
      section,
    }
    missContentWriteToFile(JSON.stringify(err))
  }
  return false
}

export const missContentWriteToFile = (content) => {
  if (!fs.existsSync('./error')) {
    fs.mkdirSync('./error')
  }
  const time = new Date().toLocaleString().replace(/\/|:/g, '-').replace(/\s/g, '+')
  fs.appendFileSync(`error/someMissContent${time}.json`, content, 'utf-8')
}

export const splitScript = (removedInvalidStr) => {
  let filtersSection = ``,
    mixinSection = '',
    propsSection = '',
    dataSection = '',
    methodsSection = '',
    computedSection = '',
    watchSection = '',
    beforeCreatedSection = '',
    createdSection = '',
    beforeMountedSection = '',
    mountedSection = '',
    destroyedSection = '',
    beforeDestroySection = ''
  const matchedMixin = removedInvalidStr.match(/mixins:(.+\n|\n)+/g)
  const isHaveMixin = matchedMixin && matchedMixin.length !== 0
  mixinSection = isHaveMixin ? matchBracketEnd(matchedMixin[0]).trim() : ``
  const matchedProps = removedInvalidStr.match(/props:(.+\n|\n)+/g)
  const isHaveProps = matchedProps && matchedProps.length !== 0
  propsSection = isHaveProps ? matchBracketEnd(matchedProps[0]).trim() : ``
  const matchedFilter = removedInvalidStr.match(/filters:(.+\n|\n)+/g)
  const isHaveFilter = matchedFilter && matchedFilter.length !== 0
  filtersSection = isHaveFilter ? matchBracketEnd(matchedFilter[0]).trim() : ``
  const matchedData = removedInvalidStr.match(/data\(\)(.+\n|\n)+/g)
  const isHaveData = matchedData && matchedData.length !== 0
  dataSection = isHaveData ? matchBracketEnd(matchedData[0].trim()) : ``
  const matchMethods = removedInvalidStr.match(/methods:(.+\n|\n)+/g)
  const isHaveMethods = matchMethods && matchMethods.length !== 0
  methodsSection = isHaveMethods ? matchBracketEnd(matchMethods[0].trim()) : ``
  const matchComputed = removedInvalidStr.match(/computed:(.+\n|\n)+/g)
  const isHaveComputed = matchComputed && matchComputed.length !== 0
  computedSection = isHaveComputed ? matchBracketEnd(matchComputed[0].trim()) : ``
  const matchWatch = removedInvalidStr.match(/watch:(.+\n|\n)+/g)
  const isHaveWatch = matchWatch && matchWatch.length !== 0
  watchSection = isHaveWatch ? matchBracketEnd(matchWatch[0].trim()) : ``
  const matchedCreated = removedInvalidStr.match(/created\(\)(.+\n|\n)+/g)
  const isHaveCreated = matchedCreated && matchedCreated.length !== 0
  createdSection = isHaveCreated ? matchBracketEnd(matchedCreated[0].trim()) : ``
  const matchedBeforeCreated = removedInvalidStr.match(/beforeCreate\(\)(.+\n|\n)+/g)
  const isHaveBeforeCreated = matchedBeforeCreated && matchedBeforeCreated.length !== 0
  beforeCreatedSection = isHaveBeforeCreated ? matchBracketEnd(matchedBeforeCreated[0].trim()) : ``
  const matchedBeforeMount = removedInvalidStr.match(/beforeMount\(\)(.+\n|\n)+/g)
  const isHaveBeforeMount = matchedBeforeMount && matchedBeforeMount.length !== 0
  beforeMountedSection = isHaveBeforeMount ? matchBracketEnd(matchedBeforeMount[0].trim()) : ``
  const matchedMounted = removedInvalidStr.match(/mounted\(\)(.+\n|\n)+/g)
  const isHaveMounted = matchedMounted && matchedMounted.length !== 0
  mountedSection = isHaveMounted ? matchBracketEnd(matchedMounted[0].trim()) : ``
  const matchedDestroyed = removedInvalidStr.match(/destroyed\(\)(.+\n|\n)+/g)
  const isHaveDestroyed = matchedDestroyed && matchedDestroyed.length !== 0
  destroyedSection = isHaveDestroyed ? matchBracketEnd(matchedDestroyed[0].trim()) : ``
  const matchedBeforeDestroy = removedInvalidStr.match(/beforeDestroy\(\)(.+\n|\n)+/g)
  const isHaveBeforeDestroy = matchedBeforeDestroy && matchedBeforeDestroy.length !== 0
  beforeDestroySection = isHaveBeforeDestroy ? matchBracketEnd(matchedBeforeDestroy[0].trim()) : ``
  return {
    filters: filtersSection,
    mixin: mixinSection,
    props: propsSection,
    data: dataSection,
    methods: methodsSection,
    computed: computedSection,
    watch: watchSection,
    beforeCreate: beforeCreatedSection,
    created: createdSection,
    beforeMount: beforeMountedSection,
    mounted: mountedSection,
    destroyed: destroyedSection,
    beforeDestroy: beforeDestroySection,
  }
}
